#include "dag.h"
#include "dag_threading.h"

#include <typeinfo>

using namespace dag;

#ifdef DAG_POSIX_THREADS

Mutex::Mutex(const char* label)
{
	int status = pthread_mutex_init(&m_mutexVar, NULL);
	if(status) {
		throw DagError("failed mutex initialization");
	}
}

Mutex::~Mutex()
{
	pthread_mutex_destroy(&m_mutexVar);
}


void Mutex::lock(void) {
	int status = pthread_mutex_lock(&m_mutexVar);
	if(status) {
		throw DagError("failed mutex lock");
	}
}

bool Mutex::tryLock() {
	int status = pthread_mutex_trylock(&m_mutexVar);	
	return (status == 0);
}

void Mutex::unlock(void) {
	int status = pthread_mutex_unlock(&m_mutexVar);
	if(status) {
		throw DagError("failed mutex unlock");
	}
}

Condition::Condition()
{
	int status = pthread_cond_init(&m_condVar, NULL);
	if(status) {
		throw DagError("failed condition initialization");
	}
}

Condition::~Condition() {
	int status = pthread_cond_destroy(&m_condVar);
	if(status) {
		throw DagError("failed condition destruction");
	}
}


void Condition::signal(void) {
	int status = pthread_cond_signal(&m_condVar);
	if(status) {
		throw DagError("failed condition signal");
	}
}


void Condition::broadcast(void)
{
	int status = pthread_cond_broadcast(&m_condVar);
	if(status)
	{
		throw DagError("failed condition broadcast");
	}
}


void Condition::wait(Mutex& mutexVar)
{
	int status = pthread_cond_wait(&m_condVar, &mutexVar.m_mutexVar);
	if(status)
	{
		throw DagError("failed condition wait");
	}
}

Thread::Thread() : m_threadId(0)
{
}

Thread::~Thread()
{
}

int Thread::create(const char* label,
					  void* (*func)(void*),
					  void* param,
					  bool detach,
					  bool asynchron)
{
	
	int status = pthread_create(&m_threadId, NULL, func, param);
	if(status)
	{
		throw DagError("failed thread creation");
	}
	
	if(detach)
	{
		pthread_detach(m_threadId);
	}
	
	if(asynchron)
	{
		int noSuccess = pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
		if(noSuccess)
		{
			throw DagError("failed thread aysnchornous cancel type");
		}
	}
	
	return status;
}

inline
void Thread::cancel()
{
	int status = pthread_cancel(m_threadId);
	if(status)
	{
		throw DagError("failed thread cancelation");
	}
}

inline
void Thread::join(void** result)
{
	int* dummy;
	if(0 == result)
	{
		result = (void**)&dummy;
	}
	
	//int status = pthread_join(m_threadId, result);
	int status = pthread_join(m_threadId, NULL);
	
	if(status)
	{
		throw DagError("failed thread join");
	}
}

int Thread::detach()
{
	return pthread_detach(m_threadId);
}

#else   
/* win32 implementation */


Mutex::Mutex(const char* label)
{
	InitializeCriticalSection ( &m_mutexVar );
}

Mutex::~CDJMutex()
{
	DeleteCriticalSection ( &m_mutexVar );
}

void Mutex::lock(void)
{
	EnterCriticalSection( &m_mutexVar );
}

bool Mutex::tryLock()
{
	BOOL result;
	result = TryEnterCriticalSection( &m_mutexVar);
	return (result != 0);
}

void Mutex::unlock(void)
{
	LeaveCriticalSection( &m_mutexVar );
}

Thread::Thread() : m_threadId(INVALID_HANDLE_VALUE) 
{
}

Thread::~CDJThread()
{
	if (m_threadId != INVALID_HANDLE_VALUE)
	{
		CloseHandle(m_threadId);
	}
}

int Thread::create(const char* label,
					  void* (*func)(void*),
					  void* param,
					  bool detach,
					  bool asynchron)
{
	if (m_threadId != INVALID_HANDLE_VALUE)
		CloseHandle(m_threadId);
	
	m_threadId = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)func, param, 0, &m_thread1);
	
	return 0;
}

inline
void Thread::cancel()
{
	TerminateThread(m_threadId, m_thread1);
}

inline
void Thread::join(void** result)
{
	WaitForSingleObject( m_threadId, INFINITE );
}


// cond. variable implementation for win32 see ..
// ref:  http://www.cs.wustl.edu/~schmidt/win32-cv-1.html   &
// ref:  http://www.cs.wustl.edu/~schmidt/win32-cv-2.html

Condition::Condition ()
{
	m_condVar.m_WaiterCount = 0;
	m_condVar.m_WasBroadcast = 0;
	m_condVar.m_Semaphore = CreateSemaphore (
									   NULL,       // no security
									   0,          // initially 0
									   0x7fffffff, // max count
									   NULL);      // unnamed
	
	InitializeCriticalSection (&m_condVar.m_WaiterCountLock);
	
	m_condVar.m_WaitersDone = CreateEvent (
										   NULL,  // no security
										   FALSE, // auto-reset
										   FALSE, // non-signaled initially
										   NULL); // unnamed
}

Condition::~CDJCondition ()
{
	if (m_condVar.m_Semaphore != INVALID_HANDLE_VALUE)
	{
		CloseHandle(m_condVar.m_Semaphore);
	}
	if (m_condVar.m_WaitersDone != INVALID_HANDLE_VALUE)
	{
		CloseHandle(m_condVar.m_WaitersDone);
	}
}

void Condition::wait (Mutex& mutexVar)
{
	EnterCriticalSection (&m_condVar.m_WaiterCountLock);
	m_condVar.m_WaiterCount++;
	LeaveCriticalSection (&m_condVar.m_WaiterCountLock);
	// The following two function calls are used in place of
	// <SignalObjectAndWait> for versions of Win32 that lack this
	// function.
	// Keep the lock held just long enough to increment the count of
	// waiters by one.  We can't keep it held across the call to
	// <WaitForSingleObject> since that will deadlock other calls to
	// <pthread_cond_signal> and <pthread_cond_broadcast>.
	LeaveCriticalSection (&mutexVar.m_mutexVar);
	// Wait to be awakened by a <pthread_cond_signal> or
	// <pthread_cond_broadcast>.
	WaitForSingleObject (m_condVar.m_Semaphore, INFINITE);
	// Reacquire lock to avoid race conditions.
	EnterCriticalSection (&m_condVar.m_WaiterCountLock);
	// We're no longer waiting...
	m_condVar.m_WaiterCount--;
	// Check to see if we're the last waiter after a
	// <pthread_cond_broadcast> call.
	int last_waiter =
	m_condVar.m_WasBroadcast && m_condVar.m_WaiterCount == 0;
	LeaveCriticalSection (&m_condVar.m_WaiterCountLock);
	// If we're the last waiter thread during this particular broadcast
	// then let all the other threads proceed.
	if (last_waiter)
		SetEvent (m_condVar.m_WaitersDone);
	// Always regain the external mutex since that's the guarantee that
	// we give to our callers.
	EnterCriticalSection (&mutexVar.m_mutexVar);
}

void Condition::wait ()
{
	EnterCriticalSection (&m_condVar.m_WaiterCountLock);
	m_condVar.m_WaiterCount++;
	LeaveCriticalSection (&m_condVar.m_WaiterCountLock);
	// Wait to be awakened by a <pthread_cond_signal> or
	// <pthread_cond_broadcast>.
	WaitForSingleObject (m_condVar.m_Semaphore, INFINITE);
	// Reacquire lock to avoid race conditions.
	EnterCriticalSection (&m_condVar.m_WaiterCountLock);
	// We're no longer waiting...
	m_condVar.m_WaiterCount--;
	// Check to see if we're the last waiter after a
	// <pthread_cond_broadcast> call.
	int last_waiter =
	m_condVar.m_WasBroadcast && m_condVar.m_WaiterCount == 0;
	LeaveCriticalSection (&m_condVar.m_WaiterCountLock);
	// If we're the last waiter thread during this particular broadcast
	// then let all the other threads proceed.
	if (last_waiter)
		SetEvent (m_condVar.m_WaitersDone);
}

void Condition::signal(void)
{
	EnterCriticalSection (&m_condVar.m_WaiterCountLock);
	int have_waiters = m_condVar.m_WaiterCount > 0;
	LeaveCriticalSection (&m_condVar.m_WaiterCountLock);
	// If there aren't any waiters, then this is a no-op.
	if (have_waiters)
		ReleaseSemaphore (m_condVar.m_Semaphore, 1, 0);
}

void Condition::broadcast(void)
{
	// This is needed to ensure that <m_WaiterCount> and <m_WasBroadcast> are
	// consistent relative to each other.
	EnterCriticalSection (&m_condVar.m_WaiterCountLock);
	int have_waiters = 0;
	if (m_condVar.m_WaiterCount > 0) {
		// We are broadcasting, even if there is just one waiter...
		// Record that we are broadcasting, which helps optimize
		// <pthread_cond_wait> for the non-broadcast case.
		m_condVar.m_WasBroadcast = 1;
		have_waiters = 1;
	}
	if (have_waiters) {
		// Wake up all the waiters atomically.
		ReleaseSemaphore (m_condVar.m_Semaphore, m_condVar.m_WaiterCount, 0);
		LeaveCriticalSection (&m_condVar.m_WaiterCountLock);
		// Wait for all the awakened threads to acquire the counting
		// semaphore.
		WaitForSingleObject (m_condVar.m_WaitersDone, INFINITE);
		// This assignment is okay, even without the <m_WaiterCountLock> held
		// because no other waiter threads can wake up to access it.
		m_condVar.m_WasBroadcast = 0;
	}
	else
		LeaveCriticalSection (&m_condVar.m_WaiterCountLock);
}

#endif //End platform specific implementation

/* TODO: should these destructors be "no-throw"? */
ScopedLock::ScopedLock(Mutex& mutex)
:	m_Mutex(mutex)
{
	m_Mutex.lock();
}
ScopedLock::~ScopedLock()
{
	m_Mutex.unlock();
}

ScopedTryLock::ScopedTryLock(Mutex& mutex)
:	m_Mutex(mutex),
m_Success(false)
{
	m_Success = m_Mutex.tryLock();
}

ScopedTryLock::~ScopedTryLock()
{
	if (m_Success)
	{
		m_Mutex.unlock();
	}
}

bool ScopedTryLock::success() const throw()
{
	return m_Success;
}

const ThreadID& Thread::id() const
{
	return m_threadId;
}

void *_gn_runnable_runthread(void *tmData)
{
	Runnable *tm;
	tm = reinterpret_cast<Runnable*>(tmData);
	tm->runThread();
	
	return NULL;
}


Runnable::Runnable()
:	m_Thread(NULL)
{
	m_Thread = new Thread;
}

Runnable::~Runnable()
{
	delete m_Thread;
}

void Runnable::startThread()
{
	m_Thread->create(typeid(*this).name(), 
					 &::_gn_runnable_runthread, 
					 (void*)(this), 
					 false);
}

void Runnable::cancelThread()
{
	m_Thread->cancel();
}

void Runnable::joinThread()
{
	m_Thread->join();
}
